home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / Apache 1.0 / src / alloc.c next >
Text File  |  1995-12-04  |  22KB  |  906 lines

  1.  
  2. /* ====================================================================
  3.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer. 
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright
  13.  *    notice, this list of conditions and the following disclaimer in
  14.  *    the documentation and/or other materials provided with the
  15.  *    distribution.
  16.  *
  17.  * 3. All advertising materials mentioning features or use of this
  18.  *    software must display the following acknowledgment:
  19.  *    "This product includes software developed by the Apache Group
  20.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  21.  *
  22.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  23.  *    endorse or promote products derived from this software without
  24.  *    prior written permission.
  25.  *
  26.  * 5. Redistributions of any form whatsoever must retain the following
  27.  *    acknowledgment:
  28.  *    "This product includes software developed by the Apache Group
  29.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  30.  *
  31.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  32.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  33.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  34.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  35.  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  42.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  43.  * ====================================================================
  44.  *
  45.  * This software consists of voluntary contributions made by many
  46.  * individuals on behalf of the Apache Group and was originally based
  47.  * on public domain software written at the National Center for
  48.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  49.  * For more information on the Apache Group and the Apache HTTP server
  50.  * project, please see <http://www.apache.org/>.
  51.  *
  52.  */
  53.  
  54.  
  55. /*
  56.  * Resource allocation code... the code here is responsible for making
  57.  * sure that nothing leaks.
  58.  *
  59.  * rst --- 4/95 --- 6/95
  60.  */
  61.  
  62. #include "conf.h"
  63. #include "alloc.h"
  64.  
  65. #include <stdarg.h>
  66.  
  67. /*****************************************************************
  68.  *
  69.  * Managing free storage blocks...
  70.  */
  71.  
  72. union align
  73. {
  74.   /* Types which are likely to have the longest RELEVANT alignment
  75.    * restrictions... we don't do much with doubles.
  76.    */
  77.   
  78.   char *cp;
  79.   void (*f)();
  80.   long l;
  81.   FILE *fp;
  82. };
  83.  
  84. #define CLICK_SZ (sizeof(union align))
  85.  
  86. union block_hdr
  87. {
  88.   union align a;
  89.   
  90.   /* Actual header... */
  91.   
  92.   struct {
  93.     char *endp;
  94.     union block_hdr *next;
  95.     char *first_avail;
  96.   } h;
  97. };
  98.  
  99. union block_hdr *block_freelist = NULL;
  100.  
  101.  
  102.  
  103. /* Get a completely new block from the system pool */
  104.  
  105. union block_hdr *malloc_block (int size)
  106. {
  107.   union block_hdr *blok =
  108.     (union block_hdr *)malloc(size + sizeof(union block_hdr));
  109.  
  110.   if (blok == NULL) return NULL;
  111.   
  112.   blok->h.next = NULL;
  113.   blok->h.first_avail = (char *)(blok + 1);
  114.   blok->h.endp = size + blok->h.first_avail;
  115.   
  116.   return blok;
  117. }
  118.  
  119.  
  120.  
  121. void chk_on_blk_list (union block_hdr *blok, union block_hdr *free_blk)
  122. {
  123.   /* Debugging code.  Left in for the moment. */
  124.     
  125.   while (free_blk) {
  126.     if (free_blk == blok) {
  127.       fprintf (stderr, "Ouch!  Freeing free block\n");
  128.       exit (1);
  129.     }
  130.     free_blk = free_blk->h.next;
  131.   }
  132. }
  133.  
  134. /* Free a chain of blocks --- must be called with alarms blocked. */
  135.  
  136. void free_blocks (union block_hdr *blok)
  137. {
  138.   /* First, put new blocks at the head of the free list ---
  139.    * we'll eventually bash the 'next' pointer of the last block
  140.    * in the chain to point to the free blocks we already had.
  141.    */
  142.   
  143.   union block_hdr *old_free_list = block_freelist;
  144.  
  145.   if (blok == NULL) return;    /* Sanity check --- freeing empty pool? */
  146.   
  147.   block_freelist = blok;
  148.   
  149.   /*
  150.    * Next, adjust first_avail pointers of each block --- have to do it
  151.    * sooner or later, and it simplifies the search in new_block to do it
  152.    * now.
  153.    */
  154.  
  155.   while (blok->h.next != NULL) {
  156.     chk_on_blk_list (blok, old_free_list);
  157.     blok->h.first_avail = (char *)(blok + 1);
  158.     blok = blok->h.next;
  159.   }
  160.  
  161.   chk_on_blk_list (blok, old_free_list);
  162.   blok->h.first_avail = (char *)(blok + 1);
  163.  
  164.   /* Finally, reset next pointer to get the old free blocks back */
  165.  
  166.   blok->h.next = old_free_list;
  167. }
  168.  
  169.  
  170.  
  171.  
  172. /* Get a new block, from our own free list if possible, from the system
  173.  * if necessary.  Must be called with alarms blocked.
  174.  */
  175.  
  176. union block_hdr *new_block (int min_size)
  177. {
  178.   union block_hdr **lastptr = &block_freelist;
  179.   union block_hdr *blok = block_freelist;
  180.   
  181.   /* First, see if we have anything of the required size
  182.    * on the free list...
  183.    */
  184.  
  185.   min_size += BLOCK_MINFREE;
  186.  
  187.   while (blok != NULL) {
  188.     if (min_size <= blok->h.endp - blok->h.first_avail) {
  189.       
  190.       *lastptr = blok->h.next;
  191.       blok->h.next = NULL;
  192.       return blok;
  193.     }
  194.     else {
  195.       lastptr = &blok->h.next;
  196.       blok = blok->h.next;
  197.     }
  198.   }
  199.  
  200.   /* Nope. */
  201.  
  202.   return malloc_block (min_size);
  203. }
  204.  
  205.  
  206.  
  207. /* Accounting */
  208.  
  209. long bytes_in_block_list (union block_hdr *blok)
  210. {
  211.   long size = 0;
  212.  
  213.   while (blok) {
  214.     size += blok->h.endp - (char *)(blok + 1);
  215.     blok = blok->h.next;
  216.   }
  217.  
  218.   return size;
  219. }
  220.  
  221.  
  222. /*****************************************************************
  223.  *
  224.  * Pool internals and management...
  225.  * NB that subprocesses are not handled by the generic cleanup code,
  226.  * basically because we don't want cleanups for multiple subprocesses
  227.  * to result in multiple three-second pauses.
  228.  */
  229.  
  230. struct process_chain;
  231. struct cleanup;
  232.  
  233. static void run_cleanups (struct cleanup *);
  234. static void free_proc_chain (struct process_chain *);
  235.  
  236. struct pool {
  237.   union block_hdr *first;
  238.   union block_hdr *last;
  239.   struct cleanup *cleanups;
  240.   struct process_chain *subprocesses;
  241.   struct pool *sub_pools;
  242.   struct pool *sub_next;
  243.   struct pool *sub_prev;
  244.   struct pool *parent;
  245.   char *free_first_avail;
  246. };
  247.  
  248. pool *permanent_pool;
  249.  
  250. /* Each pool structure is allocated in the start of its own first block,
  251.  * so we need to know how many bytes that is (once properly aligned...).
  252.  * This also means that when a pool's sub-pool is destroyed, the storage
  253.  * associated with it is *completely* gone, so we have to make sure it
  254.  * gets taken off the parent's sub-pool list...
  255.  */
  256.  
  257. #define POOL_HDR_CLICKS (1 + ((sizeof(struct pool) - 1) / CLICK_SZ))
  258. #define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)             
  259.  
  260. struct pool *make_sub_pool (struct pool *p)
  261. {
  262.   union block_hdr *blok;
  263.   pool *new_pool;
  264.  
  265.   block_alarms();
  266.   
  267.   blok = new_block (0);
  268.   new_pool = (pool *)blok->h.first_avail;
  269.   blok->h.first_avail += POOL_HDR_BYTES;
  270.  
  271.   memset ((char *)new_pool, '\0', sizeof (struct pool));
  272.   new_pool->free_first_avail = blok->h.first_avail;
  273.   new_pool->first = new_pool->last = blok;
  274.     
  275.   if (p) {
  276.     new_pool->parent = p;
  277.     new_pool->sub_next = p->sub_pools;
  278.     if (new_pool->sub_next) new_pool->sub_next->sub_prev = new_pool;
  279.     p->sub_pools = new_pool;
  280.   }
  281.   
  282.   unblock_alarms();
  283.   
  284.   return new_pool;
  285. }
  286.  
  287. void init_alloc() { permanent_pool = make_sub_pool (NULL); }
  288.  
  289. void clear_pool (struct pool *a)
  290. {
  291.   block_alarms();
  292.   
  293.   while (a->sub_pools)
  294.     destroy_pool (a->sub_pools);
  295.     
  296.   a->sub_pools = NULL;
  297.   
  298.   run_cleanups (a->cleanups);        a->cleanups = NULL;
  299.   free_proc_chain (a->subprocesses); a->subprocesses = NULL;
  300.   free_blocks (a->first->h.next);    a->first->h.next = NULL;
  301.  
  302.   a->last = a->first;
  303.   a->first->h.first_avail = a->free_first_avail;
  304.  
  305.   unblock_alarms();
  306. }
  307.  
  308. void destroy_pool (pool *a)
  309. {
  310.   block_alarms();
  311.   clear_pool (a);
  312.  
  313.   if (a->parent) {
  314.     if (a->parent->sub_pools == a) a->parent->sub_pools = a->sub_next;
  315.     if (a->sub_prev) a->sub_prev->sub_next = a->sub_next;
  316.     if (a->sub_next) a->sub_next->sub_prev = a->sub_prev;
  317.   }
  318.   
  319.   free_blocks (a->first);
  320.   unblock_alarms();
  321. }
  322.  
  323. long bytes_in_pool (pool *p) { return bytes_in_block_list (p->first); }
  324. long bytes_in_free_blocks () { return bytes_in_block_list (block_freelist); }
  325.  
  326. /*****************************************************************
  327.  *
  328.  * Allocating stuff...
  329.  */
  330.  
  331.  
  332. void *palloc (struct pool *a, int reqsize)
  333. {
  334.   /* Round up requested size to an even number of alignment units (core clicks)
  335.    */
  336.   
  337.   int nclicks = 1 + ((reqsize - 1) / CLICK_SZ);
  338.   int size = nclicks * CLICK_SZ;
  339.   
  340.   /* First, see if we have space in the block most recently
  341.    * allocated to this pool
  342.    */
  343.   
  344.   union block_hdr *blok = a->last; 
  345.   char *first_avail = blok->h.first_avail;
  346.   char *new_first_avail;
  347.  
  348.   if (size <= 0) size = 1;
  349.   new_first_avail = first_avail + size;
  350.   
  351.   if (new_first_avail <= blok->h.endp) {
  352.     blok->h.first_avail = new_first_avail;
  353.     return (void *)first_avail;
  354.   }
  355.  
  356.   /* Nope --- get a new one that's guaranteed to be big enough */
  357.   
  358.   block_alarms();
  359.   blok = new_block (size);
  360.   a->last->h.next = blok;
  361.   a->last = blok;
  362.   unblock_alarms();
  363.  
  364.   first_avail = blok->h.first_avail;
  365.   blok->h.first_avail += size;
  366.  
  367.   return (void *)first_avail;
  368. }
  369.  
  370. void *pcalloc(struct pool *a, int size)
  371. {
  372.   void *res = palloc (a, size);
  373.   memset (res, '\0', size);
  374.   return res;
  375. }
  376.  
  377. char *pstrdup(struct pool *a, char *s)
  378. {
  379.   char *res;
  380.   if (s == NULL) return NULL;
  381.   res = palloc (a, strlen(s) + 1);
  382.   strcpy (res, s);
  383.   return res;
  384. }
  385.  
  386. char *pstrcat(pool *a, ...)
  387. {
  388.   char *cp, *argp, *res;
  389.   
  390.   /* Pass one --- find length of required string */
  391.   
  392.   int len = 0;
  393.   va_list adummy;
  394.   
  395.   va_start (adummy, a);
  396.  
  397.   while ((cp = va_arg (adummy, char *)) != NULL) 
  398.     len += strlen(cp);
  399.  
  400.   va_end (adummy);
  401.  
  402.   /* Allocate the required string */
  403.  
  404.   res = (char *)palloc(a, len + 1);
  405.   cp = res;
  406.  
  407.   /* Pass two --- copy the argument strings into the result space */
  408.  
  409.   va_start (adummy, a);
  410.   
  411.   while ((argp = va_arg (adummy, char *)) != NULL) {
  412.     strcpy (cp, argp);
  413.     cp += strlen(argp);
  414.   }
  415.  
  416.   va_end (adummy);
  417.  
  418.   /* Return the result string */
  419.  
  420.   return res;
  421. }
  422.  
  423.  
  424. /*****************************************************************
  425.  *
  426.  * The 'array' functions...
  427.  */
  428.  
  429. array_header *make_array (pool *p, int nelts, int elt_size)
  430. {
  431.   array_header *res = (array_header *)palloc(p, sizeof(array_header));
  432.  
  433.   if (nelts < 1) nelts = 1;    /* Assure sanity if someone asks for
  434.                  * array of zero elts.
  435.                  */
  436.   
  437.   res->elts = pcalloc (p, nelts * elt_size);
  438.   
  439.   res->pool = p;
  440.   res->elt_size = elt_size;
  441.   res->nelts = 0;        /* No active elements yet... */
  442.   res->nalloc = nelts;        /* ...but this many allocated */
  443.  
  444.   return res;
  445. }
  446.  
  447. void *push_array (array_header *arr)
  448. {
  449.   if (arr->nelts == arr->nalloc) {
  450.     char *new_data = pcalloc (arr->pool, arr->nalloc * arr->elt_size * 2);
  451.  
  452.     memcpy (new_data, arr->elts, arr->nalloc * arr->elt_size);
  453.     arr->elts = new_data;
  454.     arr->nalloc *= 2;
  455.   }
  456.  
  457.   ++arr->nelts;
  458.   return arr->elts + (arr->elt_size * (arr->nelts - 1));
  459. }
  460.  
  461. void array_cat (array_header *dst, array_header *src)
  462. {
  463.   int elt_size = dst->elt_size;
  464.   
  465.   if (dst->nelts + src->nelts > dst->nalloc) {
  466.     int new_size = dst->nalloc * 2;
  467.     char *new_data;
  468.  
  469.     if (new_size == 0) ++new_size;
  470.     
  471.     while (dst->nelts + src->nelts > new_size)
  472.       new_size *= 2;
  473.  
  474.     new_data = pcalloc (dst->pool, elt_size * new_size);
  475.     memcpy (new_data, dst->elts, dst->nalloc * elt_size);
  476.     
  477.     dst->elts = new_data;
  478.     dst->nalloc = new_size;
  479.   }
  480.  
  481.   memcpy (dst->elts + dst->nelts * elt_size, src->elts, elt_size * src->nelts);
  482.   dst->nelts += src->nelts;
  483. }
  484.  
  485. array_header *copy_array (pool *p, array_header *arr)
  486. {
  487.   array_header *res = make_array (p, arr->nalloc, arr->elt_size);
  488.  
  489.   memcpy (res->elts, arr->elts, arr->elt_size * arr->nelts);
  490.   res->nelts = arr->nelts;
  491.   return res;
  492. }
  493.  
  494. /* This cute function copies the array header *only*, but arranges
  495.  * for the data section to be copied on the first push or arraycat.
  496.  * It's useful when the elements of the array being copied are
  497.  * read only, but new stuff *might* get added on the end; we have the
  498.  * overhead of the full copy only where it is really needed.
  499.  */
  500.  
  501. array_header *copy_array_hdr (pool *p, array_header *arr)
  502. {
  503.   array_header *res = (array_header *)palloc(p, sizeof(array_header));
  504.  
  505.   res->elts = arr->elts;
  506.   
  507.   res->pool = p;
  508.   res->elt_size = arr->elt_size;
  509.   res->nelts = arr->nelts;
  510.   res->nalloc = arr->nelts;    /* Force overflow on push */
  511.  
  512.   return res;
  513. }
  514.  
  515. /* The above is used here to avoid consing multiple new array bodies... */
  516.  
  517. array_header *append_arrays (pool *p,
  518.                  array_header *first, array_header *second)
  519. {
  520.   array_header *res = copy_array_hdr (p, first);
  521.  
  522.   array_cat (res, second);
  523.   return res;
  524. }
  525.  
  526.  
  527. /*****************************************************************
  528.  *
  529.  * The "table" functions.
  530.  */
  531.  
  532. table *make_table (pool *p, int nelts) {
  533.     return make_array (p, nelts, sizeof (table_entry));
  534. }
  535.  
  536. table *copy_table (pool *p, table *t) {
  537.     return copy_array (p, t);
  538. }
  539.  
  540. array_header *table_elts (table *t) { return t; }
  541.  
  542. char *table_get (table *t, char *key)
  543. {
  544.     table_entry *elts = (table_entry *)t->elts;
  545.     int i;
  546.  
  547.     for (i = 0; i < t->nelts; ++i)
  548.         if (!strcasecmp (elts[i].key, key))
  549.         return elts[i].val;
  550.  
  551.     return NULL;
  552. }
  553.  
  554. void table_set (table *t, char *key, char *val)
  555. {
  556.     table_entry *elts = (table_entry *)t->elts;
  557.     int i;
  558.  
  559.     for (i = 0; i < t->nelts; ++i)
  560.         if (!strcasecmp (elts[i].key, key)) {
  561.         elts[i].val = pstrdup (t->pool, val);
  562.         return;
  563.     }
  564.  
  565.     elts = (table_entry *)push_array(t);
  566.     elts->key = pstrdup (t->pool, key);
  567.     elts->val = pstrdup (t->pool, val);
  568. }
  569.  
  570. void table_merge (table *t, char *key, char *val)
  571. {
  572.     table_entry *elts = (table_entry *)t->elts;
  573.     int i;
  574.  
  575.     for (i = 0; i < t->nelts; ++i)
  576.         if (!strcasecmp (elts[i].key, key)) {
  577.         elts[i].val = pstrcat (t->pool, elts[i].val, ", ", val, NULL);
  578.         return;
  579.     }
  580.  
  581.     elts = (table_entry *)push_array(t);
  582.     elts->key = pstrdup (t->pool, key);
  583.     elts->val = pstrdup (t->pool, val);
  584. }
  585.  
  586. table* overlay_tables (pool *p, table *overlay, table *base)
  587. {
  588.     return append_arrays (p, overlay, base);
  589. }
  590.  
  591. /*****************************************************************
  592.  *
  593.  * Managing generic cleanups.  
  594.  */
  595.  
  596. struct cleanup {
  597.   void *data;
  598.   void (*plain_cleanup)(void *);
  599.   void (*child_cleanup)(void *);
  600.   struct cleanup *next;
  601. };
  602.  
  603. void register_cleanup (pool *p, void *data, void (*plain_cleanup)(void *),
  604.                void (*child_cleanup)(void *))
  605. {
  606.   struct cleanup *c = (struct cleanup *)palloc(p, sizeof (struct cleanup));
  607.   c->data = data;
  608.   c->plain_cleanup = plain_cleanup;
  609.   c->child_cleanup = child_cleanup;
  610.   c->next = p->cleanups;
  611.   p->cleanups = c;
  612. }
  613.  
  614. void kill_cleanup (pool *p, void *data, void (*cleanup)(void *))
  615. {
  616.   struct cleanup *c = p->cleanups;
  617.   struct cleanup **lastp = &p->cleanups;
  618.     
  619.   while (c) {
  620.     if (c->data == data && c->plain_cleanup == cleanup) {
  621.       *lastp = c->next;
  622.       break;
  623.     }
  624.  
  625.     lastp = &c->next;
  626.     c = c->next;
  627.   }
  628. }
  629.  
  630. void run_cleanup (pool *p, void *data, void (*cleanup)(void *))
  631. {
  632.   block_alarms();        /* Run cleanup only once! */
  633.   (*cleanup)(data);
  634.   kill_cleanup (p, data, cleanup);
  635.   unblock_alarms();
  636. }
  637.  
  638. static void run_cleanups (struct cleanup *c)
  639. {
  640.   while (c) {
  641.     (*c->plain_cleanup)(c->data);
  642.     c = c->next;
  643.   }
  644. }
  645.  
  646. static void run_child_cleanups (struct cleanup *c)
  647. {
  648.   while (c) {
  649.     (*c->child_cleanup)(c->data);
  650.     c = c->next;
  651.   }
  652. }
  653.  
  654. static void cleanup_pool_for_exec (pool *p)
  655. {
  656.   run_child_cleanups (p->cleanups);
  657.   p->cleanups = NULL;
  658.  
  659.   for (p = p->sub_pools; p; p = p->sub_next)
  660.     cleanup_pool_for_exec (p);
  661. }
  662.  
  663. void cleanup_for_exec()
  664. {
  665.   block_alarms();
  666.   cleanup_pool_for_exec (permanent_pool);
  667.   unblock_alarms();
  668. }
  669.  
  670. /*****************************************************************
  671.  *
  672.  * Files and file descriptors; these are just an application of the
  673.  * generic cleanup interface.
  674.  */
  675.  
  676. static void fd_cleanup (void *fdv) { close ((int)fdv); }
  677.  
  678. void note_cleanups_for_fd (pool *p, int fd) {
  679.   register_cleanup (p, (void *)fd, fd_cleanup, fd_cleanup);
  680. }
  681.  
  682. int popenf(struct pool *a, char *name, int flg, int mode)
  683. {
  684.   int fd;
  685.  
  686.   block_alarms();
  687.   fd = open(name, flg, mode);
  688.   if (fd >= 0) note_cleanups_for_fd (a, fd);
  689.   unblock_alarms();
  690.   return fd;
  691. }
  692.  
  693. int pclosef(struct pool *a, int fd)
  694. {
  695.   int res;
  696.   
  697.   block_alarms();
  698.   res = close(fd);
  699.   kill_cleanup(a, (void *)fd, fd_cleanup);
  700.   unblock_alarms();
  701.   return res;
  702. }
  703.  
  704. /* Note that we have separate plain_ and child_ cleanups for FILE *s,
  705.  * since fclose() would flush I/O buffers, which is extremely undesirable;
  706.  * we just close the descriptor.
  707.  */
  708.  
  709. static void file_cleanup (void *fpv) { fclose ((FILE *)fpv); }
  710. static void file_child_cleanup (void *fpv) { close (fileno ((FILE *)fpv)); }
  711.  
  712. void note_cleanups_for_file (struct pool *p, FILE *fp) {
  713.   register_cleanup (p, (void *)fp, file_cleanup, file_child_cleanup);
  714. }
  715.  
  716. FILE *pfopen(struct pool *a, char *name, char *mode)
  717. {
  718.   FILE *fd;
  719.  
  720.   block_alarms();
  721.   fd = fopen(name, mode);
  722.   if (fd != NULL) note_cleanups_for_file (a, fd);
  723.   unblock_alarms();
  724.   return fd;
  725. }
  726.  
  727. FILE *pfdopen(struct pool *a,int fd,char *mode)
  728. {
  729.   FILE *f;
  730.  
  731.   block_alarms();
  732.   f=fdopen(fd,mode);
  733.   if(f != NULL)
  734.     note_cleanups_for_file(a,f);
  735.   unblock_alarms();
  736.   return f;
  737. }
  738.  
  739.  
  740. int pfclose(struct pool *a, FILE *fd)
  741. {
  742.   int res;
  743.   
  744.   block_alarms();
  745.   res = fclose(fd);
  746.   kill_cleanup(a, (void *)fd, file_cleanup);
  747.   unblock_alarms();
  748.   return res;
  749. }
  750.  
  751. /*****************************************************************
  752.  *
  753.  * More grotty system stuff... subprocesses.  Frump.  These don't use
  754.  * the generic cleanup interface because I don't want multiple
  755.  * subprocesses to result in multiple three-second pauses; the
  756.  * subprocesses have to be "freed" all at once.  If someone comes
  757.  * along with another resource they want to allocate which has the
  758.  * same property, we might want to fold support for that into the
  759.  * generic interface, but for now, it's a special case
  760.  */
  761.  
  762. struct process_chain {
  763.   pid_t pid;
  764.   enum kill_conditions kill_how;
  765.   struct process_chain *next;
  766. };
  767.  
  768. void note_subprocess (pool *a, int pid, enum kill_conditions how)
  769. {
  770.   struct process_chain *new =
  771.     (struct process_chain *)palloc(a, sizeof(struct process_chain));
  772.  
  773.   new->pid = pid;
  774.   new->kill_how = how;
  775.   new->next = a->subprocesses;
  776.   a->subprocesses = new;
  777. }
  778.  
  779. int spawn_child (pool *p, void (*func)(void *), void *data,
  780.          enum kill_conditions kill_how,
  781.          FILE **pipe_in, FILE **pipe_out)
  782. {
  783.   int pid;
  784.   int in_fds[2];
  785.   int out_fds[2];
  786.  
  787.   block_alarms();
  788.   
  789.   if (pipe_in && pipe (in_fds) < 0)
  790.   {
  791.       unblock_alarms();
  792.       return 0;
  793.   }
  794.   
  795.   if (pipe_out && pipe (out_fds) < 0) {
  796.     if (pipe_in) {
  797.       close (in_fds[0]); close (in_fds[1]);
  798.     }
  799.     unblock_alarms();
  800.     return 0;
  801.   }
  802.  
  803.   if ((pid = fork()) < 0) {
  804.     if (pipe_in) {
  805.       close (in_fds[0]); close (in_fds[1]);
  806.     }
  807.     if (pipe_out) {
  808.       close (out_fds[0]); close (out_fds[1]);
  809.     }
  810.     unblock_alarms();
  811.     return 0;
  812.   }
  813.  
  814.   if (!pid) {
  815.     /* Child process */
  816.     
  817.     if (pipe_out) {
  818.       close (out_fds[0]);
  819.       dup2 (out_fds[1], STDOUT_FILENO);
  820.       close (out_fds[1]);
  821.     }
  822.  
  823.     if (pipe_in) {
  824.       close (in_fds[1]);
  825.       dup2 (in_fds[0], STDIN_FILENO);
  826.       close (in_fds[0]);
  827.     }
  828.  
  829.     /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */
  830.     signal (SIGCHLD, SIG_DFL);    /* Was that it? */
  831.     
  832.     func (data);
  833.     exit (0);            /* Should never get here... */
  834.   }
  835.  
  836.   /* Parent process */
  837.  
  838.   note_subprocess (p, pid, kill_how);
  839.   
  840.   if (pipe_out) {
  841.     close (out_fds[1]);
  842.     *pipe_out = fdopen (out_fds[0], "r");
  843.     
  844.     if (*pipe_out) note_cleanups_for_file (p, *pipe_out);
  845.   }
  846.  
  847.   if (pipe_in) {
  848.     close (in_fds[0]);
  849.     *pipe_in = fdopen (in_fds[1], "w");
  850.     
  851.     if (*pipe_in) note_cleanups_for_file (p, *pipe_in);
  852.   }
  853.  
  854.   unblock_alarms();
  855.   return pid;
  856. }
  857.  
  858. static void free_proc_chain (struct process_chain *procs)
  859. {
  860.   /* Dispose of the subprocesses we've spawned off in the course of
  861.    * whatever it was we're cleaning up now.  This may involve killing
  862.    * some of them off...
  863.    */
  864.  
  865.   struct process_chain *p;
  866.   int need_timeout = 0;
  867.   int status;
  868.  
  869.   if (procs == NULL) return;    /* No work.  Whew! */
  870.  
  871.   /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL
  872.    * dance with any of the processes we're cleaning up.  If we've got
  873.    * any kill-on-sight subprocesses, ditch them now as well, so they
  874.    * don't waste any more cycles doing whatever it is that they shouldn't
  875.    * be doing anymore.
  876.    */
  877.  
  878.   for (p = procs; p; p = p->next) {
  879.     if (p->kill_how == kill_after_timeout) {
  880.       /* Subprocess may be dead already.  Only need the timeout if not. */
  881.       if (kill (p->pid, SIGTERM) != -1)
  882.     need_timeout = 1;
  883.     } else if (p->kill_how == kill_always) {
  884.       kill (p->pid, SIGKILL);
  885.     }
  886.   }
  887.  
  888.   /* Sleep only if we have to... */
  889.  
  890.   if (need_timeout) sleep (3);
  891.  
  892.   /* OK, the scripts we just timed out for have had a chance to clean up
  893.    * --- now, just get rid of them, and also clean up the system accounting
  894.    * goop...
  895.    */
  896.  
  897.   for (p = procs; p; p = p->next){
  898.     
  899.     if (p->kill_how == kill_after_timeout) 
  900.       kill (p->pid, SIGKILL);
  901.  
  902.     if (p->kill_how != kill_never)
  903.       waitpid (p->pid, &status, 0);
  904.   }
  905. }
  906.